/*
 * WebSite.java
 *
 * Created on March 20, 2005, 4:32 PM
 */

package web;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import xml.XMLHelper;

/**
 *
 * @author Keith
 */
public class WebSite implements Runnable, Executor {
  
  /**
   * Holds value of property host.
   */
  final private URL host;

  /**
   * Holds value of property pool.
   */
  final private Executor pool;

  private boolean authNeeded = false;

  private boolean authObtained = false;

  final private List<Runnable> runQueue = new ArrayList<Runnable>();
  
  /**
   * Holds value of property credentials.
   */
  private WebCredentials credentials;

  /**
   *
   * @param pool
   * @param configRoot
   * @throws MalformedURLException 
   */
  public WebSite( Executor pool, Element configRoot ) throws MalformedURLException {
//    NodeList hostNodeList = siteNode.getElementsByTagName( "host" );

    final NodeList siteNodeList = configRoot. getElementsByTagName( "site" );
    final Element siteNode = (Element)siteNodeList.item( 0 );
    
//        String h = XMLHelper.getNodeValue( hostNodeList );
//        System.out.println( "h = " + h );

    this.host = new URL( XMLHelper.getNodeValue( siteNode, "host" ) );

    this.pool = pool;
    
    final NodeList credNodeList = siteNode.getElementsByTagName( "credentials" );
    if ( credNodeList != null && credNodeList.getLength() > 0 ){
      final Element cred = (Element)credNodeList.item( 0 );
      
      final URL loginPage = new URL( host, XMLHelper.getNodeValue( cred, "login-page" ) );
      final String userid = XMLHelper.getNodeValue( cred, "userid" );
      final String password = XMLHelper.getNodeValue( cred, "password" );
      final Document responseGenerator = XMLHelper.extractDOMDocument( cred, "response-generator" );
      
      final WebCredentials webCredentials = new WebCredentials( loginPage, userid, password, responseGenerator );
      
//       <login-page>/investments/</login-page>
//       <userid>kmshie</userid>
//       <password>paydolby</password>
//       <response-generator>
//System.out.println( "WebCredentials: userid=" + userid + ", password=" + password );
      this.credentials = webCredentials;
      authNeeded = true;
    }
  }

  /** Creates a new instance of WebSite
   * @param pool
   * @param configFile 
   * @throws FileNotFoundException 
   * @throws SAXException
   * @throws MalformedURLException
   * @throws IOException 
   */
  public WebSite( Executor pool, File configFile ) throws FileNotFoundException,
                                                          SAXException,
                                                          MalformedURLException,
                                                          IOException {
    this( pool, XMLHelper.parse( configFile ).getDocumentElement() );
  }
  
  /**
   * Getter for property host.
   * @return Value of property host.
   */
  public URL getHost() {
      return this.host;
  }

  /**
   * Getter for property credentials.
   * @return Value of property credentials.
   */
  public WebCredentials getCredentials() {
      return this.credentials;
  }

  /**
   * Getter for property pool.
   * @return Value of property pool.
   */
  public Executor getPool() {
      return this.pool;
  }

  protected void runBody() throws Exception {
    if ( credentials != null ){
        credentials.authenticate();
    }
  }
  
  protected void runEpilog(){
    
  }

  /**
   * When an object implementing interface <code>Runnable</code> is used 
   * to create a thread, starting the thread causes the object's 
   * <code>run</code> method to be called in that separately executing 
   * thread. 
   * <p>
   * The general contract of the method <code>run</code> is that it may 
   * take any action whatsoever.
   * 
   * @see     java.lang.Thread#run()
   */
  public void run() {
    // if authentication required, obtain it.
    // once obtained, execute any queued Runnables

//    System.out.println( "Thread " + Thread.currentThread().getName() + " start.");
    
    boolean success = true;
    
    try {
      runBody();
    } catch ( Exception e ){
      success = false;
      System.err.println( "Failure during login." );
      e.printStackTrace();
    }
    
    if ( success ){
      // sychronize at end, processing the runQueue and setting the
      // authNeeded/authObtained variables
      synchronized ( runQueue ){
        for ( Runnable task : runQueue ){
          pool.execute( task );
        }
        runQueue.clear();
        authObtained = true;

        runEpilog();

        runQueue.notifyAll();
      }
    }
//    System.out.println( "Thread " + Thread.currentThread().getName() + " end.");
    System.out.flush();
  }

  public void execute( final Runnable command ) {
    // synchronize access to the runQueue, and authNeeded/authObtained variables
    
    // if authentication required but not yet obtained, queue the Runnable.
    // if authentication not required or alredy obtained, execute the Runnable.
    
    synchronized ( runQueue ) {
      if ( authNeeded && ! authObtained ){
        runQueue.add( command );
      } else {
        pool.execute( command );
      }
      
      runQueue.notifyAll();
    }
  }

 }
